#ifndef BVH_H
#define BVH_H

#include "common/rtweekend.h"
#include "hittable.h"


class bvh_node : public hittable  {
public:
  bvh_node() {}
  bvh_node(hittable **l, int n, float time0, float time1);
  virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
  virtual bool bounding_box(float t0, float t1, aabb& box) const;
  hittable *left;
  hittable *right;
  aabb box;
};


bool bvh_node::bounding_box(float t0, float t1, aabb& b) const {
  (void)t0;
  (void)t1;

  b = box;
  return true;
}

bool bvh_node::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
  if (box.hit(r, t_min, t_max)) {
    hit_record left_rec, right_rec;
    bool hit_left = left->hit(r, t_min, t_max, left_rec);
    bool hit_right = right->hit(r, t_min, t_max, right_rec);
    if (hit_left && hit_right) {
      if (left_rec.t < right_rec.t)
        rec = left_rec;
      else
        rec = right_rec;
      return true;
    }
    else if (hit_left) {
      rec = left_rec;
      return true;
    }
    else if (hit_right) {
      rec = right_rec;
      return true;
    }
    else
      return false;
  }
  else return false;
}


int box_x_compare (const void * a, const void * b) {
  aabb box_left, box_right;
  hittable *ah = *(hittable**)a;
  hittable *bh = *(hittable**)b;
  if(!ah->bounding_box(0,0, box_left) || !bh->bounding_box(0,0, box_right))
    std::cerr << "no bounding box in bvh_node constructor\n";
  if ( box_left.min().x() - box_right.min().x() < 0.0  )
    return -1;
  else
    return 1;
}

int box_y_compare (const void * a, const void * b)
{
  aabb box_left, box_right;
  hittable *ah = *(hittable**)a;
  hittable *bh = *(hittable**)b;
  if(!ah->bounding_box(0,0, box_left) || !bh->bounding_box(0,0, box_right))
    std::cerr << "no bounding box in bvh_node constructor\n";
  if ( box_left.min().y() - box_right.min().y() < 0.0  )
    return -1;
  else
    return 1;
}

int box_z_compare (const void * a, const void * b)
{
  aabb box_left, box_right;
  hittable *ah = *(hittable**)a;
  hittable *bh = *(hittable**)b;
  if(!ah->bounding_box(0,0, box_left) || !bh->bounding_box(0,0, box_right))
    std::cerr << "no bounding box in bvh_node constructor\n";
  if ( box_left.min().z() - box_right.min().z() < 0.0  )
    return -1;
  else
    return 1;
}


bvh_node::bvh_node(hittable **l, int n, float time0, float time1) {
  int axis = int(3*random_double());
  if (axis == 0)
    qsort(l, n, sizeof(hittable *), box_x_compare);
  else if (axis == 1)
    qsort(l, n, sizeof(hittable *), box_y_compare);
  else
    qsort(l, n, sizeof(hittable *), box_z_compare);
  if (n == 1) {
    left = right = l[0];
  }
  else if (n == 2) {
    left = l[0];
    right = l[1];
  }
  else {
    left = new bvh_node(l, n/2, time0, time1);
    right = new bvh_node(l + n/2, n - n/2, time0, time1);
  }
  aabb box_left, box_right;
  if(!left->bounding_box(time0,time1, box_left) || !right->bounding_box(time0,time1, box_right))
    std::cerr << "no bounding box in bvh_node constructor\n";
  box = surrounding_box(box_left, box_right);
}

#endif
